<?php

namespace App\Kuafu\Sms\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Config\Model\ConfigModel;
use App\Kuafu\Sms\Enum\SmsCacheCodeEnum;
use App\Kuafu\Sms\Enum\SmsCodeEnum;
use App\Kuafu\Sms\Exceptions\SmsException;
use App\Kuafu\Sms\Model\SmsCodeRecordModel;
use App\Kuafu\Sms\SmsSupport\AliYun;
use App\Kuafu\User\Enum\CodeEnum;
use App\Kuafu\User\Enum\UserCodeEnum;
use App\Kuafu\User\Exceptions\UserException;
use App\Kuafu\User\Model\User;
use App\Remote\Tiger\User\UserCommonRemote;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use function AlibabaCloud\Client\json;

class SmsService
{
    public const CODE_TYPE_DEFAULT         = 0; // 默认
    public const CODE_TYPE_REGISTER        = 1; // 注册
    public const CODE_TYPE_FORGET_PASSWORD = 2; // 忘记密码
    public const CODE_TYPE_MODIFY_PASSWORD = 3; // 修改密码
    public const CODE_TYPE_MODIFY_LOGIN    = 4; // 商家APP登录
    public const CODE_TYPE_MODIFY_MOBILE   = 5; // 修改手机号
    public const CODE_TYPE_POS_LOGIN       = 6; // pos登录
    public const CODE_TYPE_MOBILE_LOGIN    = 7; // kms手机号登录-审核用
    public const CODE_TYPE_SUPPLY_LOGIN    = 8; // H5-供应商


    public static function getClient()
    {
        $clientName = config('sms.client');

        switch ($clientName) {
            case 'ali':
            default:
                return AliYun::getInstance();
        }
    }

    /**
     * 通用验证码发送
     *
     * @param string $mobile
     * @param string $code
     * @param int $type
     * @return array|string
     * @throws Exception
     */
    public static function sendCommon(string $mobile, string $code = '', int $type = self::CODE_TYPE_DEFAULT): void
    {
        //次数校验
        self::checkMinuteLimit($mobile, $type);
        self::checkMHourLimit($mobile, $type);
        // TODO 改成 短信队列
        if (empty($code)) {
            $code = (string)rand(100000, 999999);
        }

        [$templateCode, $signName] = self::getSmsConfig($type);

        $res = self::getClient()->sendCommon($mobile, ['code' => $code], $templateCode, $signName);

        Log::info("VerificationCode send mobile:{$mobile} code:{$code} res:" . json_encode($res,
                JSON_UNESCAPED_UNICODE));

        $resMessage = $res['Message'] ?? '';
        $resCode    = $res['Code'] ?? '';
        if ($resMessage == 'OK') {

            //发送成功 设置redis
            SmsService::setLimitRedis($mobile, $type);
            SmsCodeRecordModel::addRecord(
                [
                    'mobile'    => $mobile,
                    'code'      => $code,
                    'smsClient' => config('sms.client'),
                    'type'      => $type
                ]
            );

            return;
        }

        // 被限流
        if ($resCode == 'isv.BUSINESS_LIMIT_CONTROL') {
            throw new SmsException(SmsCodeEnum::SMS_CODE_MINUTE);
        }

        // 其他发送异常
        throw new SmsException(SmsCodeEnum::SMS_CODE_SEND_ERR, $resMessage);
    }

    public static function checkMinuteLimit($mobile, $type)
    {
        $prefix = SmsCacheCodeEnum::getSmsCodeMinutePrefix($mobile, $type);
        if (Redis::get($prefix) >= SmsCacheCodeEnum::SMS_CODE_MINUTE_MAX) {
            throw new SmsException(SmsCodeEnum::SMS_CODE_MINUTE);
        }
    }

    public static function checkMHourLimit($mobile, $type)
    {
        $prefix = SmsCacheCodeEnum::getSmsCodeHourPrefix($mobile, $type);
        if (Redis::get($prefix) >= SmsCacheCodeEnum::SMS_CODE_HOUR_MAX) {
            throw new SmsException(SmsCodeEnum::SMS_CODE_HOUR);
        }
    }

    /**
     * 完整版 发送验证码
     *
     * @param $mobile
     * @param $templateCode
     * @param $templateParams
     * @return array|mixed|string
     */
    public static function send($mobile, $templateCode, $templateParams, int $type = self::CODE_TYPE_DEFAULT)
    {
        return self::getClient()->send($mobile, $templateCode, $templateParams);
    }

    /**
     * 校验验证码
     *
     * @param $mobile
     * @param $code
     * @param int $type
     * @return bool
     * @throws SmsException
     */
    public static function verifyCode(string $mobile, string $code, int $type = self::CODE_TYPE_DEFAULT): bool
    {
        if (in_array(config('app.env'), ['dev', 'qa']) && $code == 111111) {
            return true;
        }

        $testCodeList = ConfigModel::getConfigByTypeKeyOne('sms', 'testCode', 10)['value'];
        $testCodeList = json_decode($testCodeList, 1);
        $testCodeList = array_column($testCodeList, null, 'mobile');

        if (key_exists($mobile, $testCodeList)) {
            $testCode = $testCodeList[$mobile]['code'] ?? '';
            if ($code == $testCode) {
                return true;
            }
        }

        $record = SmsCodeRecordModel::query()
                                    ->where('mobile', $mobile)
                                    ->where('code', $code)
                                    ->where('type', $type)
                                    ->first();

        if (empty($record) || time() > $record->expiredAt) {
            throw new SmsException(SmsCodeEnum::SMS_CODE_EXPIRE);
        }
        if (config('app.env') != 'dev') {
            $record->update(['isDeleted' => 1]);
        }
        return true;
    }

    public static function setLimitRedis($mobile, $type)
    {
        //成功后设置redis(分钟)
        Redis::setex(SmsCacheCodeEnum::getSmsCodeMinutePrefix($mobile, $type), 1 * SmsCacheCodeEnum::SMS_CODE_EXPIRE,
            1);
        //成功后设置小时
        if (Redis::get(SmsCacheCodeEnum::getSmsCodeHourPrefix($mobile, $type))) {
            Redis::incr(SmsCacheCodeEnum::getSmsCodeHourPrefix($mobile, $type));
        } else {
            Redis::setex(SmsCacheCodeEnum::getSmsCodeHourPrefix($mobile, $type), 60 * SmsCacheCodeEnum::SMS_CODE_EXPIRE,
                1);
        }
    }

    /**
     * 发送用户初始密码
     * @throws SmsException
     */
    public static function userSignUpInitPassword($mobile, $shopName, $password)
    {
        Log::info("initPassword-添加新用户发送初始密码--mobile:{$mobile} shopName:{$shopName} password:{$password}");
        $res = self::getClient()
                   ->sendCommon($mobile, [
                       'shop_name' => $shopName,
                       'password'  => $password
                   ], 'SMS_465585196', '夸父炸串');

        $resMessage = $res['Message'] ?? '';
        $resCode    = $res['Code'] ?? '';
        if ($resMessage == 'OK') {
            return;
        } else {
            Log::info("initPassword-添加新用户发送验证码--" . json_encode($res));
        }

        // 被限流
        if ($resCode == 'isv.BUSINESS_LIMIT_CONTROL') {
            throw new SmsException(SmsCodeEnum::SMS_CODE_MINUTE);
        }

        // 其他发送异常
        throw new SmsException(SmsCodeEnum::SMS_CODE_MINUTE, $resMessage);
    }

    /**
     * 发送短信
     *
     * @throws UserException
     * @throws Exception
     */
    public static function sendSms(string $mobile, int $type)
    {
        if (!preg_match("/^1[3456789]\d{9}$/", $mobile)) {
            throw new UserException(UserCodeEnum::ERR_USER_MOBILE);
        }
        $user = User::query()->where("mobile", $mobile)->first();

        if (empty($user)) {
            logMsg("电信诈骗", [
                'mobile' => $mobile,
                'type'   => $type
            ]);

            throw new UserException(UserCodeEnum::ERR_USER_MOBILE,
                UserCodeEnum::CODE_TEXT[UserCodeEnum::ERR_USER_MERCHANT_APP_LOGIN_NOT_EXIST]);
        }
        //发送短信验证码
        $code = (string)rand(100000, 999999);

        // 禁用的用户不能发送验证码
        if ($user->status == CodeEnum::USER_STATUS_STOP) {
            throw new UserException(UserCodeEnum::ERR_USER_MOBILE,
                UserCodeEnum::CODE_TEXT[UserCodeEnum::ERR_USER_STATUS_STOP]);
        }

        if (!self::checkMobileLegal($user->id, $type)) {
            throw new UserException(UserCodeEnum::ERR_USER_MOBILE,
                UserCodeEnum::CODE_TEXT[UserCodeEnum::ERR_USER_MERCHANT_APP_LOGIN_NOT_EXIST]);
        }
        // 校验类型后校验对应的用户
        SmsService::sendCommon($mobile, $code, $type);
    }


    /**
     * 短信配置
     *
     * @param int $type
     * @return array
     */
    public static function getSmsConfig(int $type): array
    {
        switch ($type) {
            case self::CODE_TYPE_MOBILE_LOGIN: //kms手机号登录-审核用
                $templateCode = config('sms.ali.appLoginAuditCodeTemplate');
                $signName     = config('sms.ali.signKmsName');
                break;
            case self::CODE_TYPE_SUPPLY_LOGIN: //供应商
                $templateCode = '';
                $signName     = config('sms.ali.signSupplyName');
                break;
            default:
                $templateCode = '';  // 默认
                $signName     = ''; // 默认
                break;
        }
        return [$templateCode, $signName];
    }

    /**
     * 校验用户手机号是否能正常发送验证码
     *
     * @throws GuzzleException
     * @throws BaseException
     */
    public static function checkMobileLegal(int $userId, int $type)
    {
        $isLegal = true;
        switch ($type) {
            case self::CODE_TYPE_SUPPLY_LOGIN:
                $isLegal = UserCommonRemote::checkCruiseCheckUser($userId)['exist'] ?? false;
                break;
            default:
                break;
        }
        return $isLegal;
    }

}
